home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
v7n11.arc
/
COMPARE.ASM
next >
Wrap
Assembly Source File
|
1988-06-21
|
61KB
|
1,350 lines
TITLE COMPARE.ASM
PAGE 60,132
;-------------------------------------------;
; COMPARE - Compares text and binary files ;
; PC Magazine - Michael J. Mefford ;
;-------------------------------------------;
_TEXT SEGMENT PUBLIC 'CODE' ;********************************;
ASSUME CS:_TEXT,DS:_TEXT ;* *;
ASSUME ES:_TEXT,SS:_TEXT ;* Requires MASM 2.0 or later *;
;* Remember to EXE2BIN *;
ORG 100H ;* *;
START: JMP MAIN ;********************************;
; DATA AREA
; ---------
SYNTAX_MSG DB CR,SPACE,SPACE,SPACE,CR,LF
COPYRIGHT DB "COMPARE 1.0 (C) 1988 Ziff Communications Co.",CR,LF
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,CR,LF
DB "Syntax: COMPARE filespec filespec[/B][/W]",CR,LF
DB "/B = Binary /W = WordStar$",CTRL_Z
TAB EQU 9
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
CRT_MODE EQU 49H
CRT_COLS EQU 4AH
ADDR_6845 EQU 63H
TEN_K EQU 10 * 1024
_64K_PARA EQU 64 * (1024 / 16)
SPEC_LENGTH EQU 80
LINE_CHAR EQU 0CDH
LF_BAR_CHAR EQU 0B5H
RT_BAR_CHAR EQU 0C6H
BLINKING EQU 10000000B
STATUS_REG DW ?
VIDEO_SEG DW 0B000H
COLS DW ?
ROWS DB 24
NORMAL_ATTRIB DB 07H
INVERSE_ATTRIB DB 70H
STRIP_MASK DB 0FFH
SYNTAX_FLAG DB 0
DISPLAY_FLAG DB 1
MISMATCH_FLAG DB 0
WINDOW_FULL DB 0
FILE_START DW FILE1_BUFFER2, FILE2_BUFFER2 ;CONSTANT
FILE_POS DW 2 DUP (?)
FILE_END DW 2 DUP (?)
BUFFER_END DW FILE1_BUFFER2 + TEN_K, FILE2_BUFFER2 + TEN_K
WINDOW_POS DW 2 DUP (?)
WINDOW_COL DW 2 DUP (?)
WINDOW_LINE DW 2 DUP (?)
WINDOW_SIZE DB ?
SAVE_COL DW 80,80
SAVE_POS DW FILE1_BUFFER2, FILE2_BUFFER2
SYNC_POS DW 2 DUP (?)
LINE_ARRAY DW 2 DUP (4 DUP (?))
LINE_CAPACITY DW ?
STATUS_LINE DW ?
HEX_SEGMENT DW 2 DUP (0)
HEX_OFFSET DW 2 DUP (0)
FILENAME DW FILESPEC + 2, FILESPEC + SPEC_LENGTH + 2
FILE_HANDLE DW 2 DUP (?)
NOT_ENOUGH DB "Requires 64K free RAM$"
BAD_MODE_MSG DB "Unsupported video mode$"
BINARY_NAME DB "COMEXE"
PROMPT DW PROMPT1, PROMPT2
PROMPT1 DB CR,LF,LF,"Enter first file name",CR,LF,"$"
PROMPT2 DB CR,LF,LF,"Enter second file name",CR,LF,"$"
NOT_FOUND_MSG DB CR,LF,"File not found$"
MENU DB "Press any key for next compare Esc to Exit",0
MENU_LENGTH EQU $ - MENU - 1
WORKING_MSG DB "Working",0
WORKING_LENGTH EQU $ - WORKING_MSG - 1
DIFF_MSG DB "Files significantly different",0
DIFF_LENGTH EQU $ - DIFF_MSG - 1
SAME_MSG DB "Files are effectively identical",0
SAME_LENGTH EQU $ - SAME_MSG - 1
DONE_MSG DB "Compare completed",0
DONE_LENGTH EQU $ - DONE_MSG - 1
; CODE AREA
; ---------
MAIN PROC NEAR
CLD ;String instructions forward.
MOV BX,_64K_PARA ;Make sure we have 64K to
MOV AH,4AH ; to work in.
INT 21H
MOV DX,OFFSET NOT_ENOUGH ;Exit if cramped, else continue.
JNC CK_SWITCH
JMP ERROR_EXIT
;--------------------------------------------------------;
; Check for /B Binary and /W WordStar switch characters. ;
;--------------------------------------------------------;
CK_SWITCH: MOV SI,81H ;Point to command line.
NEXT_SWITCH: LODSB ;Get a byte.
CMP AL,CR ;Is it carriage return?
JZ PARSE ;If yes, done here.
CMP AL,"/" ;Is it switch delimiter?
JNZ NEXT_SWITCH ;If no, next byte.
MOV BYTE PTR [SI-1],0 ;Else, ASCIIZ it out.
LODSB ;Get the switch character.
CMP AL,CR ;Make sure it's not CR
JZ PARSE ; so we don't go past end.
MOV BYTE PTR [SI-1],0 ;ASCIIZ switch character also.
AND AL,5FH ;Capitalize.
CMP AL,"W"
JNZ CK_BINARY ;If it's not "W", then skip.
MOV STRIP_MASK,7FH ;Else, we will strip high bit.
CK_BINARY: CMP AL,"B" ;Is it "B"?
JNZ NEXT_SWITCH ;If no, next byte.
MOV LINE_CAPACITY,16 ;Else, do hex compare.
JMP SHORT NEXT_SWITCH ;Next byte.
;---------------------------------------------------------------;
; Parse the command line for filespecs. If one or both missing ;
; or file can't be opened, prompt the user for filespec. ;
;---------------------------------------------------------------;
PARSE: MOV SI,81H ;Point to command line again.
XOR BP,BP ;Initialize filespec counter.
NEXT_PARSE: LODSB ;Get a byte.
CMP AL,SPACE ;Parse off leading delimiters
JA LEADING_END
CMP AL,CR ; as long as it's not ending
JNZ NEXT_PARSE ; carriage return.
LEADING_END: DEC SI ;Adjust pointer.
MOV DI,SI ;Save as filespec start position.
MOV DX,SI
FIND_END: LODSB ;Get a byte.
CMP AL,SPACE ;Are we at end of filespec?
JA FIND_END
MOV BYTE PTR [SI-1],0 ;If yes, make ASCIIZ.
PUSH SI ;Save our place.
PUSH AX ;And save the character.
CALL OPEN_FILE ;Try to open the file.
JNC RESTORE_PARSE ;If successful, get next filespec
CALL PROMPT_USER ; else, prompt user for filespec.
RESTORE_PARSE: POP AX ;Restore last parsed character
POP SI ; and position.
STORE_START: MOV FILENAME[BP],DI ;Store pointer to filename.
ADD BP,2 ;Next filespec.
CMP BP,2
JA CAP
CMP AL,CR ;Was last character parsed CR?
JNZ NEXT_PARSE ;If no, get next.
CALL PROMPT_USER ;Else prompt user for filespec
JMP SHORT STORE_START ; and store pointer to it.
;----------------------------------------------------------------------------;
; Capitalize filenames so parameter parsing can be done with one compare. ;
;----------------------------------------------------------------------------;
CAP: MOV BP,2 ;Two filenames to capitalize.
CAPITALIZE: MOV SI,FILENAME[BP] ;Point to filename.
NEXT_CAP: LODSB ;Get a byte.
CMP AL,0 ;Is it ASCIIZ?
JZ LOOP_CAP ;If yes, next name.
CMP AL,"a" ;Is it a lower case a - z?
JB NEXT_CAP
CMP AL,"z"
JA NEXT_CAP
AND BYTE PTR [SI-1],5FH ;If yes, capitalize.
JMP SHORT NEXT_CAP
LOOP_CAP: SUB BP,2 ;Capitalize both names.
JNC CAPITALIZE
;-----------------------------------------------------------------------------;
; Automatically do a binary compare if filespec has a .COM or .EXE extension. ;
;-----------------------------------------------------------------------------;
MOV BP,2
BINARY: MOV SI,FILENAME[BP] ;Point to filename.
NEXT_BINARY: LODSB ;Get a byte.
CMP AL,0 ;Is it ASCIIZ?
JZ LOOP_BINARY ;If yes, done here.
CMP AL,"." ;Is it delimiting dot char?
JNZ NEXT_BINARY ;If no, next byte.
MOV BX,2 ;Two possible binary names --
MOV DI,OFFSET BINARY_NAME ; .COM and .EXE.
NEXT_EXEC: PUSH SI ;Save our place.
PUSH DI
MOV CX,3 ;Do we have a match?
REP CMPSB
POP DI ;Restore our place.
POP SI
JZ BINARY_FILE ;If match, mark a binary compare.
ADD DI,3 ;Else point to next extension
DEC BX
JNZ NEXT_EXEC ; and check if it matches.
JMP SHORT NEXT_BINARY
LOOP_BINARY: SUB BP,2 ;Do both filenames.
JNC BINARY
JMP SHORT READY
BINARY_FILE: MOV LINE_CAPACITY,16 ;Else, do hex compare.
;---------------------------------------------------------------------;
; Line array is used to keep track of the start of file line displays ;
; so we can display three lines preceeding mismatch for context. ;
;---------------------------------------------------------------------;
READY: MOV AX,FILE_START[0] ;Initalize current line-starts
MOV LINE_ARRAY[6],AX ; of array to start of file.
MOV AX,FILE_START[2]
MOV LINE_ARRAY[14],AX
CALL VIDEO_SETUP ;Prepare for video environment.
CALL DISPLAY_SETUP ;Initialize display with heading
; and window delimiting lines.
;---------------------------------------------------------------------------;
; We will loop here filling the window with mismatches until all displayed. ;
;---------------------------------------------------------------------------;
DO_SEARCH: CALL SEARCH ;Search for mismatches.
JNC GET_KEY ;If not carry, not done yet.
CALL CLS_MENU ;If done, clear menu.
MOV DI,STATUS_LINE
ADD DI,COLS
MOV BH,NORMAL_ATTRIB
CMP MISMATCH_FLAG,1 ;Display appropriate message
JZ DO_DONE_MSG
MOV SI,OFFSET SAME_MSG
SUB DI,SAME_LENGTH AND NOT 1
JMP SHORT MESSAGE_EXIT
DO_DONE_MSG: MOV SI,OFFSET DONE_MSG
SUB DI,DONE_LENGTH AND NOT 1
JMP SHORT MESSAGE_EXIT
GET_KEY: CALL DISPLAY_MENU ;If not done, display menu.
CALL CLEAR_KEY ;Clear any awaiting keystroke.
CALL READ_KEY ;Get a keystroke.
CMP AH,1 ;Is it Esc?
JNZ DO_SEARCH ;If no, search for next mismatch.
CALL CLS_MENU ;Else, clear menu
JMP SHORT GOOD_EXIT ; and exit.
;--------------------------------------------------------------;
; Exit with appropriate message and put cursor back on screen. ;
;--------------------------------------------------------------;
ERROR_EXIT: CALL PRINT_STRING ;Print error message.
MOV AL,1 ;Exit with error level one.
JMP SHORT EXIT
MESSAGE_EXIT: CALL WRITE_STRING ;Display exit message.
GOOD_EXIT: MOV DH,ROWS ;Move cursor to next to last
DEC DH ; line of screen.
XOR DL,DL
XOR BH,BH
MOV AH,2
INT 10H
XOR AL,AL ;Error level zero.
EXIT: MOV AH,4CH ;Terminate.
INT 21H
MAIN ENDP
; ***************
; * SUBROUTINES *
; ***************
;-------------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; Carry flag = 1 if search complete ;
; Carry flag = 0 if more to search. ;
; ;
; All registers destroyed. ;
;-------------------------------------;
SEARCH PROC NEAR
;--------------------------------------------------------;
; Display "Working" message and initialization variables ;
;--------------------------------------------------------;
CALL CLS_MENU ;Clear the menu.
MOV SI,OFFSET WORKING_MSG ;Display "Working"
MOV DI,STATUS_LINE
ADD DI,80 - WORKING_LENGTH AND NOT 1
MOV BH,NORMAL_ATTRIB
OR BH,BLINKING ; with blinking attribute.
CALL WRITE_STRING
MOV AX,COLS
SHL AX,1 ;Double for attribute.
MOV DI,AX
SHL DI,1 ;Second line.
MOV WINDOW_POS[0],DI ;Initialize window pointers
MOV DL,WINDOW_SIZE
INC DL
MUL DL ; to top left corner of
ADD DI,AX ; each window.
MOV WINDOW_POS[2],DI
MOV BP,2 ;Restore the window columns,
RESTORE_WIN: MOV AX,SAVE_COL[BP] ; window lines and file
MOV WINDOW_COL[BP],AX ; positions to what they were
MOV AX,SAVE_POS[BP] ; when one of the windows
MOV FILE_POS[BP],AX ; was filled last search.
MOV AL,WINDOW_SIZE
XOR AH,AH
MOV WINDOW_LINE[BP],AX
SUB BP,2
JNC RESTORE_WIN
MOV DI,OFFSET LINE_ARRAY ;Initialize the array of
MOV AX,LINE_ARRAY[6] ; line starts to the last
MOV CX,4 ; line start from previous
REP STOSW ; search.
MOV AX,LINE_ARRAY[14]
MOV CX,4
REP STOSW
MOV WINDOW_FULL,0 ;Flag that windows are not full.
MOV DISPLAY_FLAG,0 ;No display during search.
;------------------------------------------------------------------------------;
; Check to see if End of File has been reached and/or windows have been filled ;
;------------------------------------------------------------------------------;
NEXT_SEARCH: MOV SI,FILE_POS[0] ;Get current file position.
MOV DI,FILE_POS[2]
CALL STORE_WINDOW ;See if current postion to be
CALL CK_EOF1 ; frozen for next search.
JNC CK_WINDOW2 ;If both End of Files reached
CALL CK_EOF2 ; then compare complete.
JNC CK_WIN_FULL
STC
RET
CK_WINDOW2: CALL CK_EOF2 ;If neither EOF then continue.
JNC BOTH_WINDOWS
CK_WIN_FULL: TEST WINDOW_FULL,111B ;If one EOF and windows full
JZ COMPARE ; then return, else continue.
JMP SHORT SEARCH_END
BOTH_WINDOWS: CMP WINDOW_FULL,111B ;If both windows full, return.
JZ SEARCH_END
;------------------------------------------------------------------------;
; Files are compared a byte at a time until mismatch is found. ;
; In comparing text files, if one byte is a carriage return and it is ;
; mismatch with a space then both are ignored (line wrap); else just ;
; the CR is ignored (word wrap). No special treatment for binary files. ;
;------------------------------------------------------------------------;
COMPARE: MOV BH,NORMAL_ATTRIB ;Use normal attribute.
CALL CK_EOF1 ;If either EOF then match
JC DO_MISMATCH ; not possible.
CALL CK_EOF2
JC DO_MISMATCH
MOV AL,[SI] ;Get a byte from each file.
MOV AH,[DI]
CMPSB ;Are they the same?
JZ FORMAT_BOTH ;If yes, format display.
CMP LINE_CAPACITY,16 ;If no, is it a binary compare?
JZ DO_MISMATCH ;If yes, mismatch.
CMP AL,LF ;Else, is it linefeed?
JZ ADJUST_2A ;If yes, ignore.
CMP AL,CR ;If CR, check if matched
JZ CK_ADJUST_2A ; with a space character.
CMP AH,LF ;Do the same for both files.
JZ FORMAT_1A
CMP AH,CR
JNZ DO_MISMATCH
CK_ADJUST_1A: CMP AL,SPACE ;If CR matched with space,
JZ FORMAT_BOTH ; format both characters.
ADJUST_1A: JMP SHORT FORMAT_1A ;Else, just format the CR.
CK_ADJUST_2A: CMP AH,SPACE ;Do the same for other file.
JZ FORMAT_BOTH
ADJUST_2A: XOR BP,BP ;File one index.
JMP SHORT FORMAT_2A
FORMAT_BOTH: XOR BP,BP
CALL FORMAT
FORMAT_1A: MOV BP,2 ;File two index.
MOV AL,AH ;Call format with character
FORMAT_2A: CALL FORMAT ; in AL.
JMP NEXT_SEARCH ;Check next bytes.
DO_MISMATCH: MOV MISMATCH_FLAG,1 ;Flag that mismatch found.
CALL MISMATCH ;Go highlight the mismatch.
JMP NEXT_SEARCH ;Check next bytes.
SEARCH_END: CLC
RET
SEARCH ENDP
;------------------------------------;
; INPUT ;
; AL = Character to format. ;
; BP = Index to file. ;
; ;
; OUTPUT ;
; WINDOW_COL[BP] WINDOW_LINE[BP] ;
; WINDOW_FULL FILE_POS[BP] ;
; are updated. ;
; ;
; AX, BX, CX, SI and DI preserved. ;
;------------------------------------;
FORMAT PROC NEAR
PUSH AX ;Preserve registers.
PUSH CX
PUSH SI
PUSH DI
MOV CX,WINDOW_COL[BP] ;Retrieve current column.
CMP LINE_CAPACITY,16 ;Are we working on a binary file?
JZ DO_HEX ;If yes, do a hex display.
CALL LINES ;Else, display text lines.
JMP SHORT CK_LINE_START
DO_HEX: CALL HEX
CK_LINE_START: INC FILE_POS[BP] ;Increment file position.
JCXZ LINE_START ;End of a display line?
MOV WINDOW_COL[BP],CX ;If no, store column postion
JMP SHORT FORMAT_END ; and return.
LINE_START: MOV AX,LINE_CAPACITY ;Else, go to next column.
MOV WINDOW_COL[BP],AX
CMP AX,16 ;Are we doing a binary compare?
JZ CK_ARRAY ;If yes, skip CR check.
OR BP,BP ;Else, check if EOF.
JNZ CK_CR2
CALL CK_EOF1
JMP SHORT CK_CR
CK_CR2: CALL CK_EOF2
CK_CR: JC CK_ARRAY ;If EOF, skip CR check.
MOV DI,FILE_POS[BP] ;Else, if byte at end of an 80
CMP BYTE PTR [DI],CR ; column display is CR, bump
JNZ CK_ARRAY ; file postion past it so
INC FILE_POS[BP] ; don't double space display.
CK_ARRAY: CMP DISPLAY_FLAG,1 ;Are we displaying?
JNZ ADJUST_ARRAY ;If no, skip line check.
CMP WINDOW_LINE[BP],0 ;Else, is window full?
JZ FORMAT_END ;If yes, done here.
DEC WINDOW_LINE[BP] ;Else, decrement line display.
JNZ ADJUST_ARRAY ;If window not full, skip next.
MOV CX,BP ;Else, mark appropriate
ADD CL,2 ; WINDOW_FULL bit as full.
OR WINDOW_FULL,CL
JMP SHORT FORMAT_END
ADJUST_ARRAY: TEST WINDOW_FULL,001B ;Has window been stored?
JNZ FORMAT_END ;If yes, skip array indexing.
CALL ARRAY_INDEX ;Else, index into array.
MOV SI,DI
ADD SI,2
MOV CX,3
REP MOVSW ;Move all line starts up one
MOV AX,FILE_POS[BP]
MOV [DI],AX ; and store new line start.
FORMAT_END: POP DI ;Restore registers.
POP SI
POP CX
POP AX
RET
FORMAT ENDP
;------------------------------;
; INPUT ;
; AL = Character to display. ;
; CX = current column. ;
; BP = Index to file. ;
; ;
; OUTPUT ;
; CX = new column. ;
; WINDOW_POS[BP] is updated. ;
; ;
; BX preserved. ;
;------------------------------;
LINES PROC NEAR
CMP AL,CR ;Carriage return?
JZ PAD_SPACES ;If yes, pad balance of line.
CMP AL,TAB ;Is it tab character?
JZ EXPAND_TAB ;If yes, expand to spaces.
CMP AL,LF ;Is it linefeed?
JZ LINES_END ;If yes, skip.
PUSH CX ;Save column.
MOV CX,1 ;Display one character.
CALL CK_DISPLAY
POP CX ;Restore column and decrement.
DEC CX
RET
EXPAND_TAB: PUSH CX ;Save column.
DEC CX ;Adjust column counter.
AND CX,7 ;Get bottom three bits.
INC CX ;Adjust.
PUSH CX ;Save.
CALL PAD_SPACES ;Move to next tab position.
POP AX
POP CX
SUB CX,AX ;Adjust column counter.
RET
PAD_SPACES: MOV AL,SPACE ;If CR display spaces.
CK_DISPLAY: CMP DISPLAY_FLAG,1 ;Are we to write it to screen?
JNZ DISPLAY_END ;If no, return.
CMP WINDOW_LINE[BP],0 ;Window full?
JZ DISPLAY_END ;If yes, return.
MOV DI,WINDOW_POS[BP] ;Else, retrieve display position.
WRITE_VIEW: CALL WRITE_SCREEN ;Write character CX times.
LOOP WRITE_VIEW
MOV WINDOW_POS[BP],DI ;Store new display position.
DISPLAY_END: XOR CX,CX
LINES_END: RET
LINES ENDP
;------------------------------;
; INPUT ;
; AL = Character to display. ;
; CX = current column. ;
; BP = Index to file. ;
; ;
; OUTPUT ;
; CX = new column. ;
; WINDOW_POS[BP] is updated. ;
; ;
; BX preserved. ;
;------------------------------;
HEX PROC NEAR
CMP DISPLAY_FLAG,1 ;Are we to write to screen?
JNZ DEC_COLUMN ;If no, just update column.
CMP WINDOW_LINE[BP],0 ;Else, is window full?
JNZ GO_HEX ;If yes, continue.
DEC_COLUMN: DEC CX ;Else, update column and return.
RET
GO_HEX: PUSH CX ;Save some registers.
PUSH AX
PUSH CX
MOV DI,WINDOW_POS[BP] ;Retrieve window position.
CMP CX,16 ;Is it first column?
JNZ DISP_NUMBERS ;If no, just display the byte.
PUSH AX ;Else, save character and
PUSH BX ; attribute.
MOV AX,FILE_POS[BP] ;Retrieve file position
SUB AX,FILE_START[BP] ;Subtract the starting position.
ADD AX,HEX_OFFSET[BP] ;Add the 64K offset.
PUSH AX ;Save the offset.
MOV AX,HEX_SEGMENT[BP] ;Retrieve segment.
JNC DISP_SEGMENT ;Did offset carry?
ADD AX,1000H ;If yes, add 1000h to segment.
DISP_SEGMENT: MOV BH,NORMAL_ATTRIB ;Yes normal attribute.
MOV CX,1 ;Segment and offset counter.
NEXT_ADDRESS: XCHG AH,AL ;AL = byte to display.
CALL DISPLAY_HEX
XCHG AH,AL ;Get second byte to display.
CALL DISPLAY_HEX
OR CX,CX ;Segment and offset displayed?
JZ ADDRESS_END ;If yes, done here.
MOV AL,":" ;Else, display delimiting colon.
CALL WRITE_SCREEN
POP AX ;Retrieve offset.
DEC CX ;Decrement counter.
JMP SHORT NEXT_ADDRESS ;Display segment.
ADDRESS_END: ADD DI,4 ;Move right two spaces.
POP BX ;Retrieve attribute.
POP AX ;Retrieve character.
DISP_NUMBERS: CALL DISPLAY_HEX ;Display the hex number.
MOV AL,SPACE ;Delimit with a space.
POP CX ;Retrieve column counter.
CMP CX,9 ;Is it half way?
JNZ DELIMITER
MOV AL,"-" ;If yes, display delimiting dash
DELIMITER: PUSH BX ; with normal attribute.
MOV BH,NORMAL_ATTRIB
CALL WRITE_SCREEN
POP BX ;Restore attribute.
MOV WINDOW_POS[BP],DI ;Store window position.
SHL CX,1 ;Multiply column by 8.
SHL CX,1
ADD DI,CX ;Add to current window postion.
ADD DI,30 ;Tab over additional 15.
POP AX ;Retrieve character and display.
CALL WRITE_SCREEN
POP CX ;Restore column counter
DEC CX ; and update.
JNZ HEX_END ;If last column, bump window
MOV AX,COLS ; to next line.
SUB AX,59
SHL AX,1
ADD WINDOW_POS[BP],AX
HEX_END: RET
HEX ENDP
;-------------------------------;
; INPUT ;
; AL = Hex number to display. ;
; ;
; OUTPUT ;
; none ;
; ;
; BX and CX preserved. ;
;-------------------------------;
DISPLAY_HEX PROC NEAR
PUSH CX ;Preserve CX.
MOV CX,0204H ;2 char/byte; 4 bits/byte.
ROTATE_HEX: ROL AL,CL ;Get next four bits.
PUSH AX ;Save number.
AND AL,00001111B ;Mask off high bits.
ADD AL,30H ;Convert to ASCII.
CMP AL,"9" ;Is it above 9?
JLE PRINT_HEX
ADD AL,7 ;If yes, convert to hex alpha.
PRINT_HEX: CALL WRITE_SCREEN
POP AX ;Restore number.
DEC CH ;Do both hex characters.
JNZ ROTATE_HEX
POP CX
RET
DISPLAY_HEX ENDP
;----------------------------;
; INPUT ;
; BH = Display attribute. ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;----------------------------;
MISMATCH PROC NEAR
CMP DISPLAY_FLAG,1 ;Is this first time here?
JZ SYNCHRONIZE ;If yes no context to display.
MOV DISPLAY_FLAG,1 ;Else, flag to display.
CALL CLS_WINDOWS ;Clear the windows.
MOV BP,2 ;Two windows.
NEXT_WINDOW: MOV AX,LINE_CAPACITY ;Restore line to first column.
MOV WINDOW_COL[BP],AX
MOV CX,FILE_POS[BP] ;Save current position.
CALL ARRAY_INDEX
MOV AX,[DI] ;Retrieve third line back.
MOV FILE_POS[BP],AX
NEXT_CONTEXT: MOV SI,FILE_POS[BP] ;Display context until current
CMP SI,CX ; mismatch position.
JAE LOOP_WINDOW
LODSB
CALL FORMAT
JMP SHORT NEXT_CONTEXT
LOOP_WINDOW: SUB BP,2 ;Do both windows.
JNC NEXT_WINDOW
;-----------------------------------------------------------------------------;
; Try to synchronize by matching 10 bytes up to 200 postions away from ;
; current position. In text files, ignore spaces so won't get false matches. ;
;-----------------------------------------------------------------------------;
SYNCHRONIZE: MOV BX,FILE_POS[0] ;File 1 postion in BX.
MOV CX,400 ;400 byte reach.
NEXT_SYNC: PUSH CX ;Save counter.
MOV DX,FILE_POS[2] ;Second file position in DX.
MOV CX,400 ;Also a 400 byte reach.
NEXT_TRY: PUSH CX ;Save counter.
MOV CX,10 ;Require 10 bytes for match.
MOV SI,BX ;Initialize pointers.
MOV DI,DX
NEXT_CHAR: CALL CK_EOF1 ;EOF of file one?
JC NEXT_TRY_END ;If yes, increment file 2.
CALL CK_EOF2 ;EOF of file two?
JC FILE2_END ;If yes, increment file 1.
MOV AL,[SI] ;Retrieve characters.
MOV AH,[DI]
CMPSB ;Do they match?
JZ POSSIBLE_SYNC ;If yes, poss. synchronization.
CMP LINE_CAPACITY,16 ;Else, is it binary compare?
JZ NEXT_TRY_END ;If yes, no match.
CMP AL,LF ;Else, if linefeed, ignore.
JZ ADJUST_2B
CMP AL,CR ;If carriage return, ignore
JZ CK_ADJUST_2B ; and ignore any match space.
CMP AH,LF ;Do same check for both files.
JZ ADJUST_1B
CMP AH,CR
JNZ NEXT_TRY_END
CK_ADJUST_1B: CMP AL,SPACE ;If CR with space, ignore both.
JZ NEXT_CHAR
ADJUST_1B: DEC SI ;Else, ignore CR only.
JMP SHORT NEXT_CHAR
CK_ADJUST_2B: CMP AH,SPACE ;Do same for both files.
JZ NEXT_CHAR
ADJUST_2B: DEC DI
JMP SHORT NEXT_CHAR
POSSIBLE_SYNC: CMP AL,SPACE ;If sync char is space, ignore.
JZ NEXT_CHAR
LOOP NEXT_CHAR ;Need ten matches for sync.
JMP SHORT EVALUATE
FILE2_END: POP CX ;If file 2 EOF, skip to
JMP SHORT NEXT_SYNC_END ; next file 1 postion.
NEXT_TRY_END: INC DX ;Increment file 2 postion.
POP CX ;Decrement reach counter.
LOOP NEXT_TRY ;Keep trying up to 400 positions.
NEXT_SYNC_END: INC BX ;Increment file 1 position.
POP CX ;Decrement reach counter.
LOOP NEXT_SYNC ;Keep trying up to 400 positions.
PUSH SI ;Save pointers.
PUSH DI
MOV SI,OFFSET DIFF_MSG
MOV DI,STATUS_LINE
MOV AX,COLS
SHL AX,1
SUB AX,DIFF_LENGTH * 2
ADD DI,AX
PUSH BX
PUSH DX ;Display "Files significantly
MOV BH,NORMAL_ATTRIB ; different" if didn't find
CALL WRITE_STRING ; a match.
POP DX
POP BX
POP DI
POP SI
JMP SHORT EVALUATE_EOF
;--------------------------------------------;
; Display mismatch until EOF or window full. ;
;--------------------------------------------;
EVALUATE: POP CX ;Fix stack.
POP CX
EVALUATE_EOF: CALL CK_EOF1 ;EOF of file 1?
JNC EOF2
MOV BX,FILE_END[0] ;If yes, use file end pointer.
EOF2: CALL CK_EOF2 ;EOF of file 2?
JNC INVERSE
MOV DX,FILE_END[2] ;If yes, use file end pointer.
INVERSE: MOV SYNC_POS[0],BX ;Save synchronization position.
MOV SYNC_POS[2],DX
MOV BH,INVERSE_ATTRIB ;Display in inverse attribute.
MOV BP,2 ;Two windows to display.
HIGHLIGHT: MOV SI,FILE_POS[BP] ;Is current postion = sync pos?
CMP SI,SYNC_POS[BP]
JZ LOOP_INVERSE ;If yes, done.
CMP WINDOW_LINE[BP],0 ;Is window full?
JZ LOOP_INVERSE ;If yes, done.
LODSB ;Else, display mismatch.
CALL FORMAT
JMP SHORT HIGHLIGHT
LOOP_INVERSE: SUB BP,2 ;Do both windows.
JNC HIGHLIGHT
EVALUATE_END: CALL STORE_WINDOW ;Freeze postion if window full.
MOV BP,2 ;Restore current position.
RESTORE_POS: MOV AX,SYNC_POS[BP]
MOV FILE_POS[BP],AX
SUB BP,2
JNC RESTORE_POS
RET
MISMATCH ENDP
;----------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; AX and BP destroyed. ;
;----------------------------;
STORE_WINDOW PROC NEAR
;------------------------------------------------------------------------;
; As soon as a window becomes full, freeze the position for next search. ;
;------------------------------------------------------------------------;
TEST WINDOW_FULL,110B ;Either window full?
JZ STORE_END ;If no, done.
TEST WINDOW_FULL,001B ;Else, have we already stored?
JNZ STORE_END ;If yes, done.
OR WINDOW_FULL,001B ;Else, flag stored.
MOV BP,2
FREEZE_POS: MOV AX,FILE_POS[BP] ;And store current position
MOV SAVE_POS[BP],AX
MOV AX,WINDOW_COL[BP] ; and column.
MOV SAVE_COL[BP],AX
SUB BP,2
JNC FREEZE_POS
STORE_END: RET
STORE_WINDOW ENDP
;---------------------------------;
; INPUT ;
; SI and DI = current position. ;
; Two entry points: ;
; CK_EOF1 and CK_EOF2 ;
; ;
; OUTPUT ;
; Carry flag = 1 if EOF. ;
; Carry flag = 0 if not EOF. ;
; ;
; BP destroyed. ;
;---------------------------------;
CK_EOF PROC NEAR
;-----------------------------------------------------------------;
; These two subroutines read the next 10K bytes if end of buffer ;
; reached and EOF not reached. The second buffer is moved to the ;
; first buffer and all appropriate pointers are adjusted. ;
;-----------------------------------------------------------------;
CK_EOF1: XOR BP,BP ;File index pointer.
CMP SI,FILE_END[BP] ;Is position = EOF?
JB NOT_EOF ;If no, not EOF.
CMP SI,BUFFER_END[BP] ;Else, is it at end of buffer?
JB EOF ;If no, EOF.
JMP SHORT MOVE_BUFFER ;Else, more to file; read it.
CK_EOF2: MOV BP,2 ;Do same for file 2.
CMP DI,FILE_END[BP]
JB NOT_EOF
CMP DI,BUFFER_END[BP]
JB EOF
MOVE_BUFFER: PUSH AX ;Save registers.
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV SI,FILE_START[BP] ;Retrieve buffer start address.
MOV DI,SI
SUB DI,TEN_K
MOV CX,TEN_K / 2 ;And move data to buffer
REP MOVSW ; 10K below.
CALL READ_FILE ;Read the next 10K.
SUB SAVE_POS[BP],TEN_K ;Adjust all the pointers by 10K.
SUB SYNC_POS[BP],TEN_K
SUB FILE_POS[BP],TEN_K
ADD HEX_OFFSET[BP],TEN_K ;Add 10K to offset.
JNC DO_ARRAY ;If carry, add 1000h to segment.
ADD HEX_SEGMENT[BP],1000H
DO_ARRAY: CALL ARRAY_INDEX ;Adjust line start array.
MOV CX,4
SUB_ARRAY: SUB [DI],TEN_K
INC DI
INC DI
LOOP SUB_ARRAY
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
OR BP,BP ;If file 1, adjust DI and DX.
JZ ADJUST_FILE1
SUB DI,TEN_K
SUB DX,TEN_K
JMP SHORT NOT_EOF
ADJUST_FILE1: SUB SI,TEN_K ;Else, adjust SI and BX.
SUB BX,TEN_K
NOT_EOF: CLC
RET
EOF: STC
RET
CK_EOF ENDP
;-----------------------------------;
; INPUT ;
; BP = file index. ;
; ;
; OUTPUT ;
; DI = start of line-start array. ;
; ;
; All registers preserved. ;
;-----------------------------------;
ARRAY_INDEX PROC NEAR
MOV DI,BP
SHL DI,1 ;Multiply file index by 4
SHL DI,1 ; to index into index.
ADD DI,OFFSET LINE_ARRAY
RET
ARRAY_INDEX ENDP
;-----------------------------------;
; INPUT ;
; BP = file index ;
; DX = points to ASCIIZ filename. ;
; ;
; OUTPUT ;
; Carry flag = 1 if failed. ;
; Carry flag = 0 if successful. ;
; ;
; BP preserved. ;
;-----------------------------------;
OPEN_FILE PROC NEAR
MOV AX,3D00H ;Open file for reading.
INT 21H
JC OPEN_END
MOV FILE_HANDLE[BP],AX ;Save file handle if successful
CALL READ_FILE ; and read 10K.
OPEN_END: RET
OPEN_FILE ENDP
;-----------------------------------;
; INPUT ;
; BP = file index ;
; DX = points to ASCIIZ filename. ;
; ;
; OUTPUT ;
; Carry flag = 1 if failed. ;
; Carry flag = 0 if successful. ;
; ;
; BP preserved. ;
;-----------------------------------;
READ_FILE PROC NEAR
MOV BX,FILE_HANDLE[BP] ;Retrieve filehandle.
MOV DX,FILE_START[BP] ;Point to storage buffer.
MOV SI,DX ;Save it.
MOV CX,TEN_K ;Read maximum of 10K.
MOV AH,3FH ;Read file.
INT 21H
JC READ_FILE_END ;If failed, exit.
ADD DX,AX ;Else, add bytes read to buffer
MOV FILE_END[BP],DX ; address and store as file end.
MOV CX,AX ;Bytes read in counter.
JCXZ READ_END ;Skip if zero bytes read.
MOV AL,STRIP_MASK ;Else, retrieve strip mask.
CMP AL,0FFH ;If not WordStar, skip.
JZ READ_END
WORDSTAR: AND DS:[SI],AL ;Else, strip the high bit
INC SI ; of all bytes read.
LOOP WORDSTAR
READ_END: CLC
READ_FILE_END: RET
READ_FILE ENDP
;-----------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; ES preserved. ;
;-----------------------------------;
VIDEO_SETUP PROC NEAR
PUSH ES ;Preserve ES.
MOV AX,500H ;Make sure active page is zero.
INT 10H
MOV AX,40H ;Point to the ROM BIOS data area
MOV ES,AX
MOV AL,ES:CRT_MODE ;Retrieve current video mode.
CMP AL,7 ;Is it mono mode?
JZ SUPPORTED ;If yes, continue.
CMP AL,3 ;Is it text?
JBE SUPPORTED ;If yes, continue.
UNSUPPORTED: MOV DX,OFFSET BAD_MODE_MSG ;Else, do not pass GO.
JMP ERROR_EXIT ;Go directly to jail.
SUPPORTED: MOV AH,12H
MOV BL,10H
INT 10H
CMP BL,10H ;Is there an EGA?
JZ CK_CGA ;If no, check if CGA.
TEST ES:BYTE PTR [87H],8 ;Else, EGA_info; Is it active?
JNZ CK_CGA ;If no, check CGA.
XOR BH,BH ;Else, retrieve CRT rows.
MOV AX,1130H
PUSH ES
INT 10H
POP ES
MOV ROWS,DL ;Save CRT rows.
CK_CGA: MOV AL,ES:CRT_COLS ;Retrieve CRT cols.
XOR AH,AH ;Zero in high half.
MOV DX,AX ;Save it.
MOV BX,LINE_CAPACITY ;Retrieve line capacity.
CMP BX,16 ;Are we displaying hex?
JNZ CAPACITY ;If no, store.
MOV DX,BX ;Else, use 16 as capacity.
CMP AL,40 ;Are we in 40 column mode?
JZ UNSUPPORTED ;If yes, useless display.
CAPACITY: MOV LINE_CAPACITY,DX ;Store capacity and initialize
MOV SAVE_COL[0],DX ; columns.
MOV SAVE_COL[2],DX
MOV COLS,AX ;Store columns.
SHL AX,1 ;Times two for attribute.
MOV DL,ROWS ;Retrieve rows
MUL DL ; and multiply.
MOV STATUS_LINE,AX ;Save as address of status line.
SUB DL,4 ;Subtract 4 from CRT rows
SHR DL,1 ; and divide by two
MOV WINDOW_SIZE,DL ; and save as window size.
MOV AX,ES:ADDR_6845 ;Retrieve display card.
ADD AX,6 ;Add six to get status register
MOV STATUS_REG,AX ;Store as status register.
CMP AX,3BAH ;Is it monochrome?
JZ VIDEO_END ;If yes, done here.
ADD VIDEO_SEG,800H ;Else, adjust video segment.
MOV AH,8 ;Get attribute at cursor postion.
INT 10H
MOV NORMAL_ATTRIB,AH ;And save as forground.
XOR AH,01110111B ;Flip color bits.
MOV INVERSE_ATTRIB,AH ;And save as highlight attribute.
VIDEO_END: POP ES ;Restore ES.
RET
VIDEO_SETUP ENDP
;-----------------------------------;
; INPUT ;
; AL = character to write. ;
; BH = attribute. ;
; ;
; OUTPUT ;
; None ;
; ;
; AL, BH, CX preserved. ;
;-----------------------------------;
WRITE_SCREEN PROC NEAR
PUSH ES
MOV DX,VIDEO_SEG ;Point to screen segment.
MOV ES,DX
MOV DX,STATUS_REG ;Retrieve status register.
MOV BL,AL ;Store character in BL.
HORZ_RET: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC HORZ_RET ;If not, wait until it is.
CLI ;No more interrupts.
HWAIT: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT ;If no, wait until it is.
MOV AX,BX ;Retrieve character; now it's OK
STOSW ; to write to screen buffer.
STI ;Interrupts back on.
POP ES
RET ;Return
WRITE_SCREEN ENDP
;-----------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-----------------------------------;
DISPLAY_SETUP PROC NEAR
CALL CLS ;Clear screen.
MOV DH,ROWS ;Retrieve CRT rows.
INC DH ;Move one line below off screen.
XOR DL,DL ;Column zero.
XOR BH,BH ;Page zero.
MOV AH,2 ;Set cursor position.
INT 10H
XOR DI,DI ;Point to top left of display.
MOV SI,OFFSET COPYRIGHT ;Point to copyright message.
MOV BH,NORMAL_ATTRIB ;And display it.
CALL WRITE_STRING
MOV DI,COLS ;Retrieve columns.
SHL DI,1 ;Double for attribute.
SUB DI,64 ;Right justify my name.
INC SI ;Bump pointer past linefeed.
CALL WRITE_STRING
MOV BP,0 ;Initialize counter.
NEXT_LINE: MOV CX,COLS ;Write line characters
PUSH DI ;Save position.
MOV AL,LINE_CHAR ; to screen.
NEXT_WRITE: CALL WRITE_SCREEN
LOOP NEXT_WRITE
POP DI ;Retrieve position.
CMP BP,2 ;Do two windows.
JA SETUP_END
PUSH DI ;Save screen pointer.
ADD DI,10 ;Tab in five spaces
MOV AL,LF_BAR_CHAR ; and print a left bar char.
CALL WRITE_SCREEN
MOV SI,FILENAME[BP] ;Point to filename and print it.
CALL WRITE_STRING
MOV AL,RT_BAR_CHAR ;Finish frame with right bar char
CALL WRITE_SCREEN
POP DI ;Restore display pointer.
MOV AX,COLS
SHL AX,1
MOV DL,WINDOW_SIZE
INC DL ;Multiply line length by window
MUL DL ; size plus one and add to
ADD DI,AX ; current position to get to
ADD BP,2 ; next position to display line.
JMP SHORT NEXT_LINE
SETUP_END: RET
DISPLAY_SETUP ENDP
;-----------------------------------;
; INPUT ;
; None ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-----------------------------------;
DISPLAY_MENU PROC NEAR
MOV SI,OFFSET MENU ;Point to menu.
MOV DI,STATUS_LINE ;Point to status line.
MOV BH,NORMAL_ATTRIB ;Use normal attribute
CALL WRITE_STRING ; and display menu.
RET
DISPLAY_MENU ENDP
;-----------------------------------;
; INPUT ;
; BP = file index ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-----------------------------------;
PROMPT_USER PROC NEAR
CMP SYNTAX_FLAG,1 ;If first time through, display
JZ NEXT_PROMPT ; syntax message.
MOV SYNTAX_FLAG,1
MOV DX,OFFSET SYNTAX_MSG
CALL PRINT_STRING
NEXT_PROMPT: MOV DX,PROMPT[BP] ;Retrieve appropriate prompt
CALL PRINT_STRING ; and display.
MOV DI,FILENAME[BP] ;Retrieve address of filename
MOV DX,DI ; storage and save.
SUB DX,2 ;Put in first byte of filename
MOV BX,DX ; buffer, the buffer length (80).
MOV BYTE PTR [BX],SPEC_LENGTH
MOV AH,0AH
INT 21H ;Buffered Keyboard Input.
PUSH DI ;Save filename pointer.
MOV AL,CR ;Carriage return points
ASCIIZ: SCASB ; to last byte of input.
JNZ ASCIIZ
MOV BYTE PTR [DI-1],0 ;Replace it with zero (ASCIIZ).
POP DI ;Retrieve filename pointer.
MOV DX,DI
CALL OPEN_FILE ;Attempt to open the file.
JNC PROMPT_END ;If successful, done here.
MOV DX,OFFSET NOT_FOUND_MSG ;Else, display "Not found"
CALL PRINT_STRING ; message
JMP SHORT NEXT_PROMPT ; and prompt user again.
PROMPT_END: RET
PROMPT_USER ENDP
;-------------------------------------;
; INPUT ;
; SI = points to string to display. ;
; Entry point is WRITE_STRING. ;
; ;
; OUTPUT ;
; None ;
; ;
; All registers destroyed. ;
;-------------------------------------;
WRITE_IT: CALL WRITE_SCREEN ;Write a character.
WRITE_STRING: LODSB ;Retrieve a character.
CMP AL,SPACE ;Keep writing until a carriage
JAE WRITE_IT ; return or space encountered.
RET
;-------------------;
; BIOS Keyboard I/O ;
;-------------------;
READ_KEY: MOV AH,0 ;Wait for next keyboard
INT 16H ; input.
RET
CK_KEY: MOV AH,1 ;See if character ready.
INT 16H
RET
CLEAR_IT: CALL READ_KEY ;If characters are ready
CLEAR_KEY: CALL CK_KEY ; read them to clear keyboard
JNZ CLEAR_IT ; buffer.
RET
;---------------------------;
; Screen clearing routines. ;
;---------------------------;
CLS: XOR CX,CX ;Top left corner.
MOV DX,COLS ;Right corner
DEC DL
MOV DH,ROWS ;Bottom row.
JMP SHORT CLEAR_WINDOW ;Clear the screen.
CLS_WINDOWS: MOV CX,0200H ;Row 3; column zero.
MOV DX,COLS ;Right corner.
DEC DL
MOV DH,WINDOW_SIZE ;Bottom of window one.
INC DH
CALL CLEAR_WINDOW ;Clear the window.
ADD CH,WINDOW_SIZE ;Increment a window size
INC CH ; and a delimiting line.
ADD DH,WINDOW_SIZE
INC DH
JMP SHORT CLEAR_WINDOW ;Clear second window.
CLS_MENU: MOV CH,ROWS ;Bottom row.
XOR CL,CL ;Column zero.
MOV DH,CH
MOV DL,2CH ;To column 44.
CLEAR_WINDOW: PUSH BX ;Preserve BX.
MOV BH,NORMAL_ATTRIB ;Normal attribute.
MOV AX,600H ;Scroll active page.
INT 10H
POP BX ;Restore BX.
RET
;-------------------;
; DOS print string. ;
;-------------------;
PRINT_STRING PROC NEAR
MOV AH,9
INT 21H
RET
PRINT_STRING ENDP
;----------------------------------------------------------;
; Buffered keyboard input and file buffers at end of code. ;
;----------------------------------------------------------;
EVEN
FILESPEC EQU $
FILE1_BUFFER1 EQU FILESPEC + 2 * (SPEC_LENGTH + 2)
FILE1_BUFFER2 EQU FILE1_BUFFER1 + TEN_K
FILE2_BUFFER1 EQU FILE1_BUFFER2 + TEN_K
FILE2_BUFFER2 EQU FILE2_BUFFER1 + TEN_K
_TEXT ENDS
END START